#include "bzbnet/http.h"
#include "bzbstring/stringutils.h"


bzbnet::HTTPPacket::HTTPPacket(std::string str) {
	
	if(!fields.empty()) {
		fields.clear();
	}

	*this = parse_http_packet(str);
}

std::string bzbnet::HTTPPacket::to_string() {

	std::string res;

	if(is_response()) {
		res += "HTTP/";
		if(version == 11) {
			res += "1.1 ";
		} else if(version == 20) {
			res += "2.0 ";
		} else if(version == 10) {
			res += "1.0 ";
		} else {
			throw std::runtime_error(
				"ERROR: HTTPPacket::to_string: Unexpected version number");
		}

		res += std::to_string(code);
		res += " ";
		res += status;
		res += "\n";

	} else if(is_request()) {

		res += httptype_to_string(type);
		res += " ";
		res += URI;
		res += " ";
		res += "HTTP/";
		if(version == 11) {
			res += "1.1 ";
		} else if(version == 20) {
			res += "2.0 ";
		} else if(version == 10) {
			res += "1.0 ";
		} else {
			throw std::runtime_error(
				"ERROR: HTTPPacket::to_string: Unexpected version number");
		}

		res += "\n";
	}

	for(auto iterator = fields.begin(); iterator != fields.end(); ++iterator) {
		res += iterator->first;
		res += ": ";
		res += iterator->second;
		res += "\n";
	}

	std::string end_str;
	end_str += "\n";
	end_str += 0x0d;
	end_str += 0x0a;
	res += end_str;
	res += body;

	return res;
}


unsigned int parse_version(std::string str) {

	auto slash_pos = str.find("/");

	if(slash_pos == std::string::npos) {
		throw std::runtime_error(
			"ERROR: parse_http_packet: Wrong format in HTTP header");
	}

	if(str.substr(0, slash_pos) != "HTTP") {
		throw std::runtime_error(
			"ERROR: parse_http_packet: Wrong format in HTTP header");
	}

	std::string version_str = str.substr(slash_pos + 1, str.size() - slash_pos - 1);

	if(version_str == "1.1") {
		return 11;
	} else if(version_str == "2.0") {
		return 20;
	} else if(version_str == "1.0") {
		return 10;
	} else {
		throw std::runtime_error(
			"ERROR: parse_http_packet: Unidentified HTTP version");
	}

}


bzbnet::HTTPPacket bzbnet::parse_http_packet(std::string content) {

	HTTPPacket packet;
	std::vector<std::string> lines;

	std::string end_str;
	end_str += "\n"; // NL
	end_str += 0x0d; // CL
	end_str += 0x0a; // LF
	auto end_it = content.find(end_str);
	size_t end_index = content.size();

	if(end_it != std::string::npos) {
		end_index = end_it;

		packet.body = content.substr(end_index, content.size() - end_index);

	}

	std::string header = content.substr(0, end_index);
	lines = bzbstring::split(header, '\n');

	if(lines.size() < 2) {
		
		throw std::runtime_error(
			"ERROR: parse_http_packet: Wrong format in HTTP header");

	} else {

		if(lines[0] == "") {
			throw std::runtime_error(
				"ERROR: parse_http_packet: Wrong format in HTTP header");
		}

		// Parse first line of header
		
		auto tokens = bzbstring::split(lines[0], ' ');
		if(tokens.size() < 3) {
			throw std::runtime_error(
				"ERROR: parse_http_packet: Wrong format in HTTP header");
		}

		if(tokens[0].find("HTTP") == std::string::npos) {
			// Request ?
			if(tokens[tokens.size() - 1].find("HTTP") != std::string::npos) {

				const std::string &request_type = tokens[0];
				if(request_type == "GET") {
					packet.type = HTTPType::GET;
				} else if(request_type == "HEAD") {
					packet.type = HTTPType::HEAD;
				} else if(request_type == "POST") {
					packet.type = HTTPType::POST;
				} else if(request_type == "PUT") {
					packet.type = HTTPType::PUT;
				} else if(request_type == "DELETE") {
					packet.type = HTTPType::DELETE;
				} else if(request_type == "CONNECT") {
					packet.type = HTTPType::CONNECT;
				} else if(request_type == "OPTIONS") {
					packet.type = HTTPType::OPTIONS;
				} else if(request_type == "TRACE") {
					packet.type = HTTPType::TRACE;
				} else {
					throw std::runtime_error(
						"ERROR: parse_http_packet: Wrong format in HTTP header");
				}

				packet.URI = tokens[1];
				packet.version = parse_version(tokens[tokens.size() - 1]);

			} else {
				throw std::runtime_error(
				"ERROR: parse_http_packet: Wrong format in HTTP header");
			}

		} else {
			// Response ?
			packet.code = std::stoi(tokens[1]);
			packet.status = tokens[2];
			packet.version = parse_version(tokens[0]);
			packet.type = HTTPType::NONE;
		}
	}

	for(size_t i = 1; i < lines.size(); i++) {

		if(lines[i] == "" || lines[i].find(":") == std::string::npos) {
			throw std::runtime_error(
				"ERROR: parse_http_packet: Wrong format in HTTP header");
		}

		std::string field_key;
		std::string field_data;
		auto split_index = lines[i].find(":");
		field_key = lines[i].substr(0, split_index);
		field_data = lines[i].substr(split_index + 1, lines[i].size() - split_index);
		while(field_data.at(0) == ' ') {
			field_data = field_data.substr(1, field_data.size() - 1);
		}

		packet.fields.insert(std::pair<std::string, std::string>
							(field_key, field_data));
	}

	return packet;
}
